﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;

namespace Beaker.Injection
{

	/// <summary>
	/// Allows querying the container for instances
	/// </summary>
	[ContractClass(typeof(QueryContainerContracts))]
	public interface IQueryContainer
	{
		/// <summary>
		/// Gets an instance of the given <typeparamref name="TService"/>.
		/// </summary>
		/// <typeparam name="TService">Type of object requested.</typeparam>
		/// <returns>
		/// The requested service instance.
		/// </returns>
		/// <exception cref="T:SimpleInjector.ActivationException">Thrown when there are errors resolving the service instance.</exception>
		TService GetInstance<TService>()
			where TService : class;

		/// <summary>
		/// Gets an instance of the given <paramref name="serviceType"/>.
		/// </summary>
		/// <param name="serviceType">Type of object requested.</param>
		/// <returns>
		/// The requested service instance.
		/// </returns>
		/// <exception cref="T:SimpleInjector.ActivationException">Thrown when there are errors resolving the service instance.</exception>
		object GetInstance(Type serviceType);

		/// <summary>
		/// Gets all instances of the given <typeparamref name="TService"/> currently registered in the container.
		/// 
		/// </summary>
		/// <typeparam name="TService">Type of object requested.</typeparam>
		/// <returns>
		/// A sequence of instances of the requested TService.
		/// </returns>
		/// <exception cref="T:SimpleInjector.ActivationException">Thrown when there are errors resolving the service instance.</exception>
		IEnumerable<TService> GetAllInstances<TService>();

		/// <summary>
		/// Gets all instances of the given <paramref name="serviceType"/> currently registered in the container.
		/// 
		/// </summary>
		/// <param name="serviceType">Type of object requested.</param>
		/// <returns>
		/// A sequence of instances of the requested serviceType.
		/// </returns>
		/// <exception cref="T:SimpleInjector.ActivationException">Thrown when there are errors resolving the service instance.</exception>
		IEnumerable<object> GetAllInstances(Type serviceType);
	}

	/// <summary>
	/// Code Contracts for the <see cref="IQueryContainer"/> interface.
	/// </summary>
	[ContractClassFor(typeof(IQueryContainer))]
	abstract class QueryContainerContracts
		: IQueryContainer
	{
		public TService GetInstance<TService>()
			where TService : class
		{
			Contract.Ensures(Contract.Result<TService>() != null, "Should throw ActivationException when no service could be returned");

			throw new NotImplementedException();
		}

		public object GetInstance(Type serviceType)
		{
			Contract.Requires<ArgumentNullException>(serviceType != null, "serviceType");
			Contract.Ensures(Contract.Result<object>() != null);

			throw new NotImplementedException();
		}

		public IEnumerable<TService> GetAllInstances<TService>()
		{
			Contract.Ensures(Contract.Result<IEnumerable<TService>>() != null);
			Contract.Ensures(Contract.ForAll(Contract.Result<IEnumerable<TService>>(), s => s != null));

			throw new NotImplementedException();
		}

		public IEnumerable<object> GetAllInstances(Type serviceType)
		{
			Contract.Requires<ArgumentNullException>(serviceType != null, "serviceType");
			Contract.Ensures(Contract.Result<IEnumerable<object>>() != null);
			Contract.Ensures(Contract.ForAll(Contract.Result<IEnumerable<object>>(), s => s != null));

			throw new NotImplementedException();
		}
	}

}
